
package it.uniroma3.dia.digraph.io.util;

import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.graphdrawing.graphml.xmlns.GraphmlType;
import org.xml.sax.SAXException;

/**
 * Defines I/O operations
 * @author BarycentricCoords
 *
 */
public class IoManager{
	
	public static final String JAXB_OBJECTS_PACKAGE = 
		"org.graphdrawing.graphml.xmlns";
	
	public static final String XML_SCHEMA_LOCATION = 
		"xml"+System.getProperty("file.separator")+
		"xsd"+System.getProperty("file.separator")+
		"graphml.xsd";
	
	
	/**
	 * Loads a Graphml graph from a file
	 * @param filePath the absolute path of the file where the graph is stored in
	 * @return a Graphml representation of a graph
	 * @throws IOException if any error occurs
	 */
	public GraphmlType loadFromFile(String filePath) throws IOException {
		GraphmlType graph = null;
		Unmarshaller unmarshaller = this.getUnmarshaller();
		try {

			StreamSource source = new StreamSource(new File(filePath));
			JAXBElement<GraphmlType> graphmlElement = unmarshaller.unmarshal(source, GraphmlType.class);
			graph = graphmlElement.getValue();						
		
		} catch (JAXBException e) {
			
			throw new IOException("\nError while loading from: "+filePath+
					"\nMessage: "+e.getLocalizedMessage()
					+"\nCause: "+e.getCause()
					+"\nCode: "+e.getErrorCode());
		}
		Initializer.init(graph.getGraph());
		return graph;
	}
	
	
	/**
	 * Creates and returns an {@link Unmarshaller} to read a graph from a file
	 * @return an {@link Unmarshaller}
	 * @throws IOException if any I/O error occurs
	 */
	private Unmarshaller getUnmarshaller() throws IOException{
		Unmarshaller unmarshaller = null;
		
			try {
				JAXBContext context = JAXBContext.newInstance(JAXB_OBJECTS_PACKAGE);
				unmarshaller = context.createUnmarshaller();
				
				SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
				try {
					Schema schema = schemaFactory.newSchema(new File(XML_SCHEMA_LOCATION));
					unmarshaller.setSchema(schema);					
					
					unmarshaller.setEventHandler(new ValidationEventHandler(){
						public boolean handleEvent(ValidationEvent ve) {
							//	logs warnings but continues reading
							
							if (ve.getSeverity()==ValidationEvent.WARNING){										
								ValidationEventLocator vel = ve.getLocator();
								System.err.println(
										"Line:Col[" + vel.getLineNumber()
										+ ":" + vel.getColumnNumber()
										+ "]:" + ve.getMessage());
								return true;
							}									
							//	if any error happens, stops loading									
							return false;
						}
					});
				} catch (SAXException e) {
					throw new IOException("Can't validate input file: "+e.getLocalizedMessage());
				}
			} catch (JAXBException e) {
				throw new IOException("Error while preparing dataloading:\n"+e.getLocalizedMessage());
			}	
		
		return unmarshaller;
	}

	
	/**
	 * Saves a {@link GraphmlType} graph object to a file
	 * @param graph the graph to save
	 * @param filePath the absolute path of the file where the graph has to be stored in
	 * @throws IOException if any I/O error occurs
	 */
	public void saveToFile(GraphmlType graph, String filePath) throws IOException{
		Marshaller marshaller = this.getMarshaller();
		try {
			File out = new File(filePath);
			out.createNewFile();
			
			marshaller.marshal(graph, new FileWriter(out));
			
		} catch (JAXBException e) {
			throw new IOException("Error while writing "+filePath+":\n"+
					e.getLocalizedMessage()+
					"\nCause: "+e.getCause());
		}
		
	}

	/**
	 * Creates and returns a {@link Marshaller} to save a {@link GraphmlType} graph to a file
	 * @return a {@link Marshaller}
	 * @throws IOException if any I/O error occurs
	 */
	private Marshaller getMarshaller() throws IOException {
		Marshaller marshaller = null;
		try {
			JAXBContext context = JAXBContext.newInstance(JAXB_OBJECTS_PACKAGE);
			marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);	
		} catch (JAXBException e) {			
			throw new IOException(e.getLocalizedMessage());
		}
		return marshaller;
	}



}

	